from flask import Flask, render_template, abort, send_file import os import json from typing import List import markdown as md from dataclasses import dataclass from dataclasses_json import DataClassJsonMixin app = Flask(__name__) @dataclass class ArtifactItem(DataClassJsonMixin): file: str description: str width: int = 0 @dataclass class Artifact(DataClassJsonMixin): id: str date: str changelog: str artifacts: List[ArtifactItem] hash: str = None @dataclass class ProjectInfo(DataClassJsonMixin): name: str commit_url: str class Project: id: str info: ProjectInfo def get_artifacts(self) -> List[Artifact]: result = [] artifacts_path = os.path.join("../builds", self.id, "artifacts") artifact_folders = sorted([folder.path for folder in os.scandir( artifacts_path) if folder.is_dir()], reverse=True) for artifact_folder in artifact_folders: info_file_path = os.path.join(artifact_folder, "info.json") if not os.path.exists(info_file_path): continue try: with open(info_file_path, "r", encoding="utf-8") as f: artifact = Artifact.from_json(f.read()) artifact.date = artifact.date.strip() maxwidth = max((len(a.file) for a in artifact.artifacts), default=0) for a in artifact.artifacts: a.width = maxwidth result.append(artifact) except: continue return result def get_projects() -> List[Project]: result = [] projects = [f.path for f in os.scandir("../builds") if f.is_dir()] for project in projects: info_path = os.path.join(project, "info.json") if not os.path.exists(info_path): continue try: proj = Project() _, proj.id = os.path.split(project) with open(info_path, "r", encoding="utf-8") as f: proj.info = ProjectInfo.from_json(f.read()) result.append(proj) except: continue return result @app.route("/projects///") def download_item(project_id, artifact_id, download_item): file_path = os.path.join("../builds", project_id, "artifacts", artifact_id, download_item) if not os.path.exists(file_path): return abort(404) return send_file(file_path) @app.route("/projects/") def display_project(project_id): projects = get_projects() selected_project = next( (project for project in projects if project.id == project_id), None) if not selected_project: return abort(404) info_path = os.path.join("../builds", selected_project.id, "info.md") readme = None try: if os.path.exists(info_path): with open(info_path, "r", encoding="utf-8") as f: readme = md.markdown(f.read()) except: readme = None artifacts = selected_project.get_artifacts() return render_template("project_view.html", projects=projects, selected_project=selected_project, readme=readme, artifacts=artifacts) @app.route("/") def index(): projects = get_projects() return render_template("main.html", projects=projects) if __name__ == "__main__": app.run(host='0.0.0.0')