parent
c31062fc70
commit
9cf95398f8
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,224 @@ |
||||
// |
||||
// CloudConvert.swift |
||||
// Padel Tournament |
||||
// |
||||
// Created by Razmig Sarkissian on 14/09/2023. |
||||
// |
||||
|
||||
import Foundation |
||||
|
||||
class CloudConvert { |
||||
|
||||
enum CloudConvertionError: LocalizedError { |
||||
case unknownError |
||||
case serviceError(ErrorResponse) |
||||
case urlNotFound(String) |
||||
|
||||
var errorDescription: String? { |
||||
switch self { |
||||
case .unknownError: |
||||
return "Erreur" |
||||
case .serviceError(let errorResponse): |
||||
return errorResponse.error |
||||
case .urlNotFound(let url): |
||||
return "L'URL [\(url)] n'est pas valide" |
||||
} |
||||
} |
||||
} |
||||
|
||||
static let manager = CloudConvert() |
||||
|
||||
func uploadFile(_ url: URL) async throws -> String { |
||||
let taskResponse = try await createJob(url) |
||||
let uploadResponse = try await uploadFile(taskResponse, url: url) |
||||
var fileReady = false |
||||
while fileReady == false { |
||||
try await Task.sleep(nanoseconds: 3_000_000_000) |
||||
let progressResponse = try await checkFile(id: uploadResponse.data.id) |
||||
if progressResponse.data.step == "finish" && progressResponse.data.stepPercent == 100 { |
||||
fileReady = true |
||||
print("progressResponse.data.minutes", progressResponse.data.minutes) |
||||
} |
||||
} |
||||
|
||||
let convertedFile = try await downloadConvertedFile(id: uploadResponse.data.id) |
||||
return convertedFile |
||||
} |
||||
|
||||
func createJob(_ url: URL) async throws -> TaskResponse { |
||||
guard let taskURL = URL(string: "https://api.convertio.co/convert") else { |
||||
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert") |
||||
} |
||||
var request: URLRequest = URLRequest(url: taskURL) |
||||
let parameters = """ |
||||
{"apikey":"d97cf13ef6d163e5e386c381fc8d256f","input":"upload","file":"","filename":"","outputformat":"csv","options":""} |
||||
""" |
||||
|
||||
|
||||
let postData = parameters.data(using: .utf8) |
||||
request.httpMethod = "POST" |
||||
request.httpBody = postData |
||||
|
||||
let task = try await URLSession.shared.data(for: request) |
||||
print("tried: \(request.url)") |
||||
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: task.0) { |
||||
print("errorResponse.error", errorResponse.error) |
||||
throw CloudConvertionError.serviceError(errorResponse) |
||||
} |
||||
|
||||
return try JSONDecoder().decode(TaskResponse.self, from: task.0) |
||||
} |
||||
|
||||
func uploadFile(_ response: TaskResponse, url: URL) async throws -> UploadResponse { |
||||
guard let uploadTaskURL = URL(string: "https://api.convertio.co/convert/\(response.data.id)/\(url.encodedLastPathComponent)") else { |
||||
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(response.data.id)/\(url.encodedLastPathComponent)") |
||||
} |
||||
var uploadRequest: URLRequest = URLRequest(url: uploadTaskURL) |
||||
uploadRequest.httpMethod = "PUT" |
||||
let uploadTask = try await URLSession.shared.upload(for: uploadRequest, fromFile: url) |
||||
|
||||
print("tried: \(uploadRequest.url)") |
||||
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: uploadTask.0) { |
||||
print("errorResponse.error", errorResponse.error) |
||||
throw CloudConvertionError.serviceError(errorResponse) |
||||
} |
||||
|
||||
return try JSONDecoder().decode(UploadResponse.self, from: uploadTask.0) |
||||
} |
||||
|
||||
func checkFile(id: String) async throws -> ProgressResponse { |
||||
guard let taskURL = URL(string: "https://api.convertio.co/convert/\(id)/status") else { |
||||
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(id)/status") |
||||
} |
||||
var request: URLRequest = URLRequest(url: taskURL) |
||||
request.httpMethod = "GET" |
||||
let task = try await URLSession.shared.data(for: request) |
||||
|
||||
print("tried: \(request.url)") |
||||
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: task.0) { |
||||
print("errorResponse.error", errorResponse.error) |
||||
throw CloudConvertionError.serviceError(errorResponse) |
||||
} |
||||
|
||||
return try JSONDecoder().decode(ProgressResponse.self, from: task.0) |
||||
} |
||||
|
||||
func downloadConvertedFile(id: String) async throws -> String { |
||||
// try await Task.sleep(nanoseconds: 3_000_000_000) |
||||
|
||||
guard let downloadTaskURL = URL(string: "https://api.convertio.co/convert/\(id)/dl/base64") else { |
||||
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(id)/dl/base64") |
||||
} |
||||
var downloadRequest: URLRequest = URLRequest(url: downloadTaskURL) |
||||
downloadRequest.httpMethod = "GET" |
||||
let downloadTask = try await URLSession.shared.data(for: downloadRequest) |
||||
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: downloadTask.0) { |
||||
print("errorResponse.error", errorResponse.error) |
||||
throw CloudConvertionError.serviceError(errorResponse) |
||||
} |
||||
|
||||
print("tried: \(downloadRequest.url)") |
||||
let dataResponse = try JSONDecoder().decode(DataResponse.self, from: downloadTask.0) |
||||
if let decodedData = Data(base64Encoded: dataResponse.data.content), let string = String(data: decodedData, encoding: .utf8) { |
||||
return string |
||||
} |
||||
|
||||
throw CloudConvertionError.unknownError |
||||
} |
||||
} |
||||
|
||||
// MARK: - DataResponse |
||||
struct DataResponse: Decodable { |
||||
let code: Int |
||||
let status: String |
||||
let data: DataDownloadClass |
||||
} |
||||
|
||||
// MARK: - DataClass |
||||
struct DataDownloadClass: Decodable { |
||||
let id, encode, content: String |
||||
} |
||||
|
||||
|
||||
// MARK: - ErrorResponse |
||||
struct ErrorResponse: Decodable { |
||||
let code: Int |
||||
let status, error: String |
||||
} |
||||
|
||||
|
||||
// MARK: - TaskResponse |
||||
struct TaskResponse: Decodable { |
||||
let code: Int |
||||
let status: String |
||||
let data: DataClass |
||||
} |
||||
|
||||
// MARK: - DataClass |
||||
struct DataClass: Decodable { |
||||
let id: String |
||||
} |
||||
|
||||
// MARK: - ProgressResponse |
||||
struct ProgressResponse: Decodable { |
||||
let code: Int |
||||
let status: String |
||||
let data: ProgressDataClass |
||||
} |
||||
|
||||
// MARK: - DataClass |
||||
struct ProgressDataClass: Decodable { |
||||
let id, step: String |
||||
let stepPercent: Int |
||||
let minutes: String |
||||
|
||||
enum CodingKeys: String, CodingKey { |
||||
case id, step |
||||
case stepPercent = "step_percent" |
||||
case minutes |
||||
} |
||||
|
||||
init(from decoder: Decoder) throws { |
||||
let container = try decoder.container(keyedBy: CodingKeys.self) |
||||
id = try container.decode(String.self, forKey: .id) |
||||
step = try container.decode(String.self, forKey: .step) |
||||
minutes = try container.decode(String.self, forKey: .minutes) |
||||
if let value = try? container.decode(String.self, forKey: .stepPercent) { |
||||
print(value) |
||||
stepPercent = Int(value) ?? 0 |
||||
} else { |
||||
stepPercent = try container.decode(Int.self, forKey: .stepPercent) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// MARK: - Output |
||||
struct Output: Decodable { |
||||
let url: String |
||||
let size: String |
||||
} |
||||
|
||||
// MARK: - UploadResponse |
||||
struct UploadResponse: Decodable { |
||||
let code: Int |
||||
let status: String |
||||
let data: UploadDataClass |
||||
} |
||||
|
||||
// MARK: - DataClass |
||||
struct UploadDataClass: Decodable { |
||||
let id, file: String |
||||
let size: Int |
||||
} |
||||
|
||||
extension URL { |
||||
var encodedLastPathComponent: String { |
||||
if #available(iOS 17.0, *) { |
||||
lastPathComponent |
||||
} else { |
||||
lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? lastPathComponent |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,160 @@ |
||||
// |
||||
// SwiftParser.swift |
||||
// Padel Tournament |
||||
// |
||||
// Created by Razmig Sarkissian on 01/09/2023. |
||||
// |
||||
|
||||
import Foundation |
||||
|
||||
typealias LineIterator = AsyncLineSequence<URL.AsyncBytes>.AsyncIterator |
||||
struct Line: Identifiable { |
||||
let id: UUID = UUID() |
||||
let date: Date |
||||
let source: URL |
||||
let lineNumber: Int |
||||
let rawValue: String |
||||
let data: [String?] |
||||
|
||||
func updatePlayer(player: Player) { |
||||
player.clubName = data[11] |
||||
player.ligue = data[9] |
||||
player.country = data[4] |
||||
player.license = data[5] |
||||
player.inscriptionRank = rank |
||||
player.currentRank = rank |
||||
player.tournamentCount = Int16(tournamentCount) |
||||
player.points = points ?? 0 |
||||
player.codeClub = data[10] |
||||
player.assimilation = assimilation |
||||
} |
||||
|
||||
var points: Double? { |
||||
if let pointsValue { |
||||
return Double(pointsValue.replacingOccurrences(of: ",", with: ".")) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
var pointsValue: String? { |
||||
data[6] ?? nil |
||||
} |
||||
|
||||
var tournamentCountValue: String? { |
||||
data[8] ?? nil |
||||
} |
||||
|
||||
var tournamentCount: Int64 { |
||||
if let tournamentCountValue { |
||||
return Int64(tournamentCountValue) ?? 0 |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
var rankValue: String? { |
||||
data[1] ?? nil |
||||
} |
||||
|
||||
var rank: Int64 { |
||||
if let rankValue { |
||||
return Int64(rankValue) ?? 0 |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
var assimilation: String? { |
||||
data[7] |
||||
} |
||||
} |
||||
|
||||
struct CSVParser: AsyncSequence, AsyncIteratorProtocol { |
||||
typealias Element = Line |
||||
|
||||
private let url: URL |
||||
private var lineIterator: LineIterator |
||||
private let seperator: Character |
||||
private let quoteCharacter: Character = "\"" |
||||
private var lineNumber = 0 |
||||
private let date: Date |
||||
let maleData: Bool |
||||
|
||||
init(url: URL, seperator: Character = ";") { |
||||
self.date = url.dateFromPath |
||||
self.url = url |
||||
self.seperator = seperator |
||||
self.lineIterator = url.lines.makeAsyncIterator() |
||||
self.maleData = url.path().contains(PDFSource.messieurs.rawValue) |
||||
} |
||||
|
||||
mutating func last() async throws -> Line? { |
||||
var lastString: String? |
||||
var currentString: String? |
||||
var isOver: Bool = false |
||||
while isOver == false { |
||||
lastString = currentString |
||||
currentString = try await lineIterator.next() |
||||
if currentString == nil { |
||||
isOver = true |
||||
} |
||||
} |
||||
|
||||
if let lastString { |
||||
return Line( |
||||
date: date, |
||||
source: url, |
||||
lineNumber: lineNumber, |
||||
rawValue: lastString, |
||||
data: split(line: lastString)) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
mutating func next() async throws -> Line? { |
||||
if let string = try await lineIterator.next() { |
||||
defer { lineNumber += 1 } |
||||
return Line( |
||||
date: date, |
||||
source: url, |
||||
lineNumber: lineNumber, |
||||
rawValue: string, |
||||
data: split(line: string) |
||||
) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func makeAsyncIterator() -> CSVParser { |
||||
return self |
||||
} |
||||
|
||||
private func split(line: String) -> [String?] { |
||||
var data = [String?]() |
||||
var inQuote = false |
||||
var currentString = "" |
||||
|
||||
for character in line { |
||||
switch character { |
||||
case quoteCharacter: |
||||
inQuote = !inQuote |
||||
continue |
||||
|
||||
case seperator: |
||||
if !inQuote { |
||||
data.append(currentString.isEmpty ? nil : currentString) |
||||
currentString = "" |
||||
continue |
||||
} |
||||
|
||||
default: |
||||
break |
||||
} |
||||
|
||||
currentString.append(character) |
||||
} |
||||
|
||||
data.append(currentString.isEmpty ? nil : currentString) |
||||
|
||||
return data |
||||
} |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
// |
||||
// FileImportView.swift |
||||
// PadelClub |
||||
// |
||||
// Created by Razmig Sarkissian on 24/03/2024. |
||||
// |
||||
|
||||
import SwiftUI |
||||
|
||||
struct FileImportView: View { |
||||
var body: some View { |
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) |
||||
} |
||||
} |
||||
|
||||
#Preview { |
||||
FileImportView() |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
// |
||||
// UpdateSourceRankDateView.swift |
||||
// PadelClub |
||||
// |
||||
// Created by Razmig Sarkissian on 25/03/2024. |
||||
// |
||||
|
||||
import SwiftUI |
||||
|
||||
struct UpdateSourceRankDateView: View { |
||||
var body: some View { |
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) |
||||
} |
||||
} |
||||
|
||||
#Preview { |
||||
UpdateSourceRankDateView() |
||||
} |
||||
Loading…
Reference in new issue