main.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import os
  2. from pathlib import Path
  3. import typer
  4. from archive_logs import run as run_archive_logs
  5. from clients import run
  6. from files import run as run_files
  7. from move_files import default_desktop, refresh_and_move
  8. from session_details import run as run_session_details
  9. from summary import run as run_summary
  10. from watch import run as run_watch
  11. app = typer.Typer(help="PC-Visit Filetracker")
  12. DATA_DIRECTORY = Path("data")
  13. def default_session_history() -> Path:
  14. user_profile = Path(os.environ.get("USERPROFILE", str(Path.home())))
  15. return user_profile / "AppData" / "Roaming" / "pcvisit Data" / "Session History"
  16. @app.callback(invoke_without_command=True)
  17. def app_callback(context: typer.Context) -> None:
  18. """Verwalten der PC-Visit-Filetracker-Befehle."""
  19. if context.invoked_subcommand is None:
  20. typer.echo(context.get_help())
  21. @app.command("clients")
  22. def clients_command(
  23. directory: Path | None = typer.Argument(None, help="Verzeichnis mit XML-Dateien"),
  24. output: Path = typer.Option(DATA_DIRECTORY / "clients.csv", "--output", "-o", help="Zielpfad der CSV-Datei"),
  25. ) -> None:
  26. """Importiert Client-Daten aus Session-XML-Dateien."""
  27. directory = directory or default_session_history()
  28. try:
  29. result = run(directory, output)
  30. except ValueError as error:
  31. raise typer.BadParameter(str(error), param_hint="directory") from error
  32. typer.echo(f"{result.xml_count} XML-Dateien gelesen, {result.row_count} Einträge nach {output} geschrieben.")
  33. @app.command("files")
  34. def files_command(
  35. directory: Path | None = typer.Argument(None, help="Verzeichnis mit XML-Dateien"),
  36. output: Path = typer.Option(DATA_DIRECTORY / "files.csv", "--output", "-o", help="Zielpfad der CSV-Datei"),
  37. ignore_timestamp: bool = typer.Option(
  38. False,
  39. "--ignore-timestamp",
  40. help="Ignoriert den Abgleich des Erstellungsdatums mit der Session.",
  41. ),
  42. ) -> None:
  43. """Importiert vorhandene Dateien aus ReceivedFile-Elementen."""
  44. directory = directory or default_session_history()
  45. try:
  46. result = run_files(directory, output, ignore_timestamp=ignore_timestamp)
  47. except ValueError as error:
  48. raise typer.BadParameter(str(error), param_hint="directory") from error
  49. typer.echo(
  50. f"{result.xml_count} XML-Dateien gelesen, {result.row_count} vorhandene Dateien nach {output} geschrieben."
  51. )
  52. @app.command("move-files")
  53. def move_files_command(
  54. directory: Path | None = typer.Argument(None, help="Verzeichnis mit XML-Dateien"),
  55. files_csv: Path = typer.Option(
  56. DATA_DIRECTORY / "files.csv", "--files-csv", help="CSV-Datei mit den gefundenen Dateien"
  57. ),
  58. clients_csv: Path = typer.Option(DATA_DIRECTORY / "clients.csv", "--clients-csv", help="CSV-Datei mit den Kunden"),
  59. desktop: Path | None = typer.Option(None, "--desktop", help="Desktop-Verzeichnis als Zielbasis"),
  60. ignore_timestamp: bool = typer.Option(
  61. False,
  62. "--ignore-timestamp",
  63. help="Ignoriert den Abgleich des Erstellungsdatums mit der Session.",
  64. ),
  65. ) -> None:
  66. """Verschiebt gefundene Dateien in Desktop/PC-Visit/Kunde."""
  67. directory = directory or default_session_history()
  68. try:
  69. result = refresh_and_move(
  70. directory,
  71. files_csv,
  72. clients_csv,
  73. desktop or default_desktop(),
  74. ignore_timestamp,
  75. )
  76. except ValueError as error:
  77. raise typer.BadParameter(str(error)) from error
  78. typer.echo(
  79. f"{result.moved_count} Dateien verschoben, {result.skipped_count} übersprungen, "
  80. f"{result.missing_client_count} ohne Kunde."
  81. )
  82. @app.command("archive-logs")
  83. def archive_logs_command(
  84. directory: Path | None = typer.Argument(None, help="Verzeichnis mit XML-Dateien"),
  85. ) -> None:
  86. """Archiviert Session-XML-Dateien nach Jahr."""
  87. directory = directory or default_session_history()
  88. try:
  89. result = run_archive_logs(directory)
  90. except ValueError as error:
  91. raise typer.BadParameter(str(error), param_hint="directory") from error
  92. typer.echo(
  93. f"{result.checked_count} XML-Dateien geprüft, {result.archived_count} archiviert, "
  94. f"{result.skipped_count} übersprungen."
  95. )
  96. @app.command("summary")
  97. def summary_command(
  98. input_path: Path = typer.Option(
  99. DATA_DIRECTORY / "session_details.csv",
  100. "--input",
  101. "-i",
  102. help="Eingabe-CSV aus session-details",
  103. ),
  104. output: Path = typer.Option(DATA_DIRECTORY / "summary.csv", "--output", "-o", help="Zielpfad der CSV-Datei"),
  105. days: int = typer.Option(14, "--days", help="Anzahl der letzten Kalendertage für die Konsolenausgabe"),
  106. ) -> None:
  107. """Erstellt eine Zusammenfassung aus session_details.csv."""
  108. try:
  109. result = run_summary(input_path, output, days)
  110. except ValueError as error:
  111. raise typer.BadParameter(str(error), param_hint="input_path") from error
  112. typer.echo(
  113. f"{result.activity_count} Aktivitäten gelesen, {result.summary_count} Zusammenfassungen nach {output} geschrieben."
  114. )
  115. if result.recent_summary:
  116. typer.echo(result.recent_summary)
  117. @app.command("session-details")
  118. def session_details_command(
  119. directory: Path | None = typer.Argument(None, help="Verzeichnis mit XML-Dateien"),
  120. output: Path = typer.Option(
  121. DATA_DIRECTORY / "session_details.csv",
  122. "--output",
  123. "-o",
  124. help="Zielpfad der CSV-Datei",
  125. ),
  126. ) -> None:
  127. """Sammelt UserActivity-Details aus Session-XML-Dateien."""
  128. directory = directory or default_session_history()
  129. try:
  130. result = run_session_details(directory, output)
  131. except ValueError as error:
  132. raise typer.BadParameter(str(error), param_hint="directory") from error
  133. typer.echo(
  134. f"{result.xml_count} XML-Dateien geprüft, {result.activity_count} UserActivity-Einträge nach {output} geschrieben."
  135. )
  136. @app.command("watch")
  137. def watch_command(
  138. directory: Path | None = typer.Argument(None, help="Verzeichnis mit XML-Dateien"),
  139. files_csv: Path = typer.Option(
  140. DATA_DIRECTORY / "files.csv", "--files-csv", help="CSV-Datei mit den gefundenen Dateien"
  141. ),
  142. clients_csv: Path = typer.Option(DATA_DIRECTORY / "clients.csv", "--clients-csv", help="CSV-Datei mit den Kunden"),
  143. desktop: Path | None = typer.Option(None, "--desktop", help="Desktop-Verzeichnis als Zielbasis"),
  144. ignore_timestamp: bool = typer.Option(
  145. False,
  146. "--ignore-timestamp",
  147. help="Ignoriert den Abgleich des Erstellungsdatums mit der Session.",
  148. ),
  149. ) -> None:
  150. """Überwacht XML-Dateien und führt bei Änderungen move-files aus."""
  151. directory = directory or default_session_history()
  152. try:
  153. run_watch(directory, files_csv, clients_csv, desktop or default_desktop(), ignore_timestamp)
  154. except ValueError as error:
  155. raise typer.BadParameter(str(error), param_hint="directory") from error
  156. if __name__ == "__main__":
  157. app()