jon 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 880c52018870d05e4d10fd370690086f86eef18ff326bd8fbe1e910f1c4334c9
4
+ data.tar.gz: 3c558c63986bbba5ac7415e0bb17ac590d75e8d52389f46f76d7a3d3420f3fa8
5
+ SHA512:
6
+ metadata.gz: 2aec4de1e112fff7d879c4911a0228b2535d61ddef92346d4b77c4b1d88ea56eebf396ef6246c080f8e667b9e117eeeec891facf984baef3e369962d6e53b97b
7
+ data.tar.gz: 4eb453336031ec0b6a35eb2f7fb39bf7b4a0d6c05ab99479d53794067bed7f9e20095ba5698d17e3aa78637a3f0b774cfe9fe15ab02e14890e202fbe1e091e48
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in jon.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,128 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jon (1.0.0)
5
+ logging (~> 2.3.0)
6
+ mini_exiftool_vendored (~> 9.2.7.v1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.2)
12
+ backport (1.2.0)
13
+ benchmark (0.2.0)
14
+ binding_ninja (0.2.3)
15
+ coderay (1.1.3)
16
+ debase (0.2.4.1)
17
+ debase-ruby_core_source (>= 0.10.2)
18
+ debase-ruby_core_source (0.10.14)
19
+ diff-lcs (1.5.0)
20
+ e2mmap (0.1.0)
21
+ jaro_winkler (1.5.4)
22
+ kramdown (2.3.2)
23
+ rexml
24
+ kramdown-parser-gfm (1.1.0)
25
+ kramdown (~> 2.0)
26
+ little-plugger (1.1.4)
27
+ logging (2.3.0)
28
+ little-plugger (~> 1.1)
29
+ multi_json (~> 1.14)
30
+ mini_exiftool (2.10.2)
31
+ mini_exiftool_vendored (9.2.7.v1)
32
+ mini_exiftool (>= 1.6.0)
33
+ mocha (1.13.0)
34
+ multi_json (1.15.0)
35
+ nokogiri (1.13.3-x86_64-linux)
36
+ racc (~> 1.4)
37
+ parallel (1.22.1)
38
+ parser (3.1.1.0)
39
+ ast (~> 2.4.1)
40
+ proc_to_ast (0.1.0)
41
+ coderay
42
+ parser
43
+ unparser
44
+ racc (1.6.0)
45
+ rainbow (3.1.1)
46
+ rake (13.0.6)
47
+ regexp_parser (2.2.1)
48
+ reverse_markdown (2.1.1)
49
+ nokogiri
50
+ rexml (3.2.5)
51
+ rspec (3.11.0)
52
+ rspec-core (~> 3.11.0)
53
+ rspec-expectations (~> 3.11.0)
54
+ rspec-mocks (~> 3.11.0)
55
+ rspec-core (3.11.0)
56
+ rspec-support (~> 3.11.0)
57
+ rspec-expectations (3.11.0)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.11.0)
60
+ rspec-mocks (3.11.0)
61
+ diff-lcs (>= 1.2.0, < 2.0)
62
+ rspec-support (~> 3.11.0)
63
+ rspec-parameterized (0.5.1)
64
+ binding_ninja (>= 0.2.3)
65
+ parser
66
+ proc_to_ast
67
+ rspec (>= 2.13, < 4)
68
+ unparser
69
+ rspec-support (3.11.0)
70
+ rubocop (1.26.1)
71
+ parallel (~> 1.10)
72
+ parser (>= 3.1.0.0)
73
+ rainbow (>= 2.2.2, < 4.0)
74
+ regexp_parser (>= 1.8, < 3.0)
75
+ rexml
76
+ rubocop-ast (>= 1.16.0, < 2.0)
77
+ ruby-progressbar (~> 1.7)
78
+ unicode-display_width (>= 1.4.0, < 3.0)
79
+ rubocop-ast (1.16.0)
80
+ parser (>= 3.1.1.0)
81
+ rubocop-rspec (2.9.0)
82
+ rubocop (~> 1.19)
83
+ ruby-debug-ide (0.7.3)
84
+ rake (>= 0.8.1)
85
+ ruby-progressbar (1.11.0)
86
+ solargraph (0.44.3)
87
+ backport (~> 1.2)
88
+ benchmark
89
+ bundler (>= 1.17.2)
90
+ diff-lcs (~> 1.4)
91
+ e2mmap
92
+ jaro_winkler (~> 1.5)
93
+ kramdown (~> 2.3)
94
+ kramdown-parser-gfm (~> 1.1)
95
+ parser (~> 3.0)
96
+ reverse_markdown (>= 1.0.5, < 3)
97
+ rubocop (>= 0.52)
98
+ thor (~> 1.0)
99
+ tilt (~> 2.0)
100
+ yard (~> 0.9, >= 0.9.24)
101
+ thor (1.2.1)
102
+ tilt (2.0.10)
103
+ unicode-display_width (2.1.0)
104
+ unparser (0.6.4)
105
+ diff-lcs (~> 1.3)
106
+ parser (>= 3.1.0)
107
+ webrick (1.7.0)
108
+ yard (0.9.27)
109
+ webrick (~> 1.7.0)
110
+
111
+ PLATFORMS
112
+ x86_64-linux
113
+
114
+ DEPENDENCIES
115
+ debase (~> 0.2.4.1)
116
+ jon!
117
+ mocha (~> 1.13.0)
118
+ rake (~> 13.0)
119
+ rspec (~> 3.0)
120
+ rspec-core (~> 3.11.0)
121
+ rspec-parameterized (~> 0.5.1)
122
+ rubocop (~> 1.21)
123
+ rubocop-rspec (~> 2.9.0)
124
+ ruby-debug-ide (~> 0.7.3)
125
+ solargraph (~> 0.44.3)
126
+
127
+ BUNDLED WITH
128
+ 2.3.8
data/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # Jon
2
+
3
+ ## Installation
4
+
5
+ Exécuter :
6
+
7
+ $ bundle install
8
+
9
+ ## Développement
10
+
11
+ Aprés avoir récupéré le repository, exécuter `bin/setup` pour installer les dépendances. Puis lancer `rake spec` pour exécuter les tests automatisés.
12
+
13
+ Pour installer cette gem sur la machine, exécuter `bundle exec rake install`.
14
+ Pour release une nouvelle version, mettre à jour le numéro de version dans `version.rb`, puis exécuter `bundle exec rake release` ce qui crée un nouveau tag git pour la version, push les commit et crée un tag et push le `.gem` vers [rubygems.org](https://rubygems.org).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/jon.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/jon/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "jon"
7
+ spec.version = Jon::VERSION
8
+ spec.authors = ["JordanC0TT"]
9
+ spec.email = ["jordan.content@pm.me"]
10
+
11
+ spec.summary = "Catalogue des fichiers"
12
+ spec.description = "Permet de cataloguer, restaurer des fichiers photos et vidéos"
13
+ spec.homepage = "https://github.com/JordanC0TT/jon"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
+ `git ls-files -z`.split("\x0").reject do |f|
22
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
23
+ end
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ # Uncomment to register a new dependency of your gem
30
+ spec.add_development_dependency "debase", "~> 0.2.4.1"
31
+ spec.add_development_dependency "mocha", "~> 1.13.0"
32
+ spec.add_development_dependency "rspec-core", "~> 3.11.0"
33
+ spec.add_development_dependency "rspec-parameterized", "~> 0.5.1"
34
+ spec.add_development_dependency "rubocop-rspec", "~> 2.9.0"
35
+ spec.add_development_dependency "ruby-debug-ide", "~> 0.7.3"
36
+ spec.add_development_dependency "solargraph", "~> 0.44.3"
37
+ spec.add_dependency "logging", "~> 2.3.0"
38
+ spec.add_dependency "mini_exiftool_vendored", "~> 9.2.7.v1"
39
+
40
+ # For more information and examples about making a new gem, check out our
41
+ # guide at: https://bundler.io/guides/creating_gem.html
42
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Catalogage
4
+ # Cataloger
5
+ class Catalog
6
+ def initialize(analyse, nom_attributeur, application, verificateur)
7
+ @analyse = analyse
8
+ @nom_attributeur = nom_attributeur
9
+ @application = application
10
+ @verificateur = verificateur
11
+ @log = Logging.logger["Cataloguer"]
12
+ end
13
+
14
+ def process(dossier, apply, destination)
15
+ @analyse.analyser(dossier)
16
+ @nom_attributeur.attribut(@analyse.fichiers_analyses)
17
+ @verificateur.index(@analyse.fichiers_analyses, destination)
18
+ @log.info "Fichiers en conflits : #{@verificateur.conflit.keys}"
19
+ if apply && !@verificateur.conflit.size.positive?
20
+ @application.applique(@analyse.fichiers_analyses, destination)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/regex_helpers"
4
+
5
+ require "images/catalogage/fichier"
6
+
7
+ module Catalogage
8
+ module Etape
9
+ # Analyse
10
+ class Analyse
11
+ attr_reader :fichiers_analyses
12
+
13
+ def initialize(extracteur, exif_manipulateur, fichiers_analyses = {})
14
+ @extracteur = extracteur
15
+ @exif_manipulateur = exif_manipulateur
16
+ @fichiers_analyses = fichiers_analyses
17
+ @log = Logging.logger["Analyse"]
18
+ end
19
+
20
+ def analyser(dossier)
21
+ @log.debug "Parcours du dossier '#{dossier}'"
22
+ Dir.each_child(dossier) do |nom_fichier|
23
+ fichier = "#{dossier}/#{nom_fichier}"
24
+ if File.file?(fichier)
25
+ if File.extname(fichier) =~ RegexHelpers::EXTENSIONS_EXCLUS
26
+ date_extraite = nil
27
+ exif = false
28
+ if @exif_manipulateur.datetimeoriginal?(fichier)
29
+ date_extraite = @exif_manipulateur.get_datetimeoriginal(fichier)
30
+ exif = true
31
+ elsif @extracteur.extirpabilite(fichier)
32
+ date_extraite = @extracteur.extraction_du(File.basename(fichier, File.extname(fichier)))
33
+ else
34
+ @log.warn "Aucune date extraire pour le fichier '#{fichier}'"
35
+ end
36
+ @log.debug "Le fichier '#{fichier}' à une date extraite au #{date_extraite}"
37
+ @log.debug "La date extraite viens des metadata 'datetimeoriginal' : #{exif ? "oui" : "non"}"
38
+ ajoute_analyse Fichier.new(fichier, File.extname(fichier), date_extraite, exif)
39
+ else
40
+ @log.warn "Le fichier '#{fichier}' ne sera pas analysé"
41
+ end
42
+ else
43
+ analyser(fichier)
44
+ next
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def ajoute_analyse(fichier_analyse)
52
+ path_destination = fichier_analyse.path_destination
53
+ if @fichiers_analyses.key?(path_destination)
54
+ @fichiers_analyses.merge!({ path_destination =>
55
+ @fichiers_analyses
56
+ .fetch(path_destination)
57
+ .push(fichier_analyse) })
58
+ else
59
+ @fichiers_analyses.store(path_destination, [].push(fichier_analyse))
60
+ end
61
+ @log.info "Le fichier '#{fichier_analyse.path}' sera versé vers #{path_destination}"
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Catalogage
4
+ module Etape
5
+ class Application
6
+ def initialize(exif_manipulateur)
7
+ @exif_manipulateur = exif_manipulateur
8
+ @log = Logging.logger["Application"]
9
+ end
10
+
11
+ def applique(fichiers_par_dossier, destination)
12
+ fichiers_par_dossier.each_pair do |dossier, fichiers|
13
+ @log.debug "Application sur le dossier '#{dossier}'"
14
+ destination_par_dossier = "#{destination}/#{dossier}"
15
+ @log.info "destination '#{destination_par_dossier}'"
16
+ fichiers.each do |fichier|
17
+ @log.debug "Application sur le fichier '#{fichier.path}'"
18
+ @exif_manipulateur.set_datetimeoriginal(fichier.path, fichier.date_extraite) unless fichier.exif
19
+ nouveau_nom = fichier.path_nouveau_nom(File.dirname(fichier.path))
20
+ File.rename(fichier.path, nouveau_nom)
21
+ FileUtils.mkdir_p(destination_par_dossier)
22
+ FileUtils.move(nouveau_nom, fichier.path_nouveau_nom(destination_par_dossier))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/nommage/nom_attributeur"
4
+ require "images/nommage/dedoublonneur"
5
+
6
+ module Catalogage
7
+ module Etape
8
+ # Dedoublonne et attribut un nom
9
+ class NomAttribuer
10
+ def attribut(fichiers_analyses_par_dossier)
11
+ fichiers_analyses_par_dossier.each_value do |dossier|
12
+ dedoublonneur = Dedoublonneur.new
13
+ dossier.each do |fichier_analyse|
14
+ if fichier_analyse.date_extraite.nil?
15
+ fichier_analyse.nom_attribue = dedoublonneur.dedoublonne_par_numerotation(File.basename(
16
+ fichier_analyse.path, fichier_analyse.extension
17
+ ))
18
+ else
19
+ fichier_analyse.nom_attribue = dedoublonneur.dedoublonne_par_numerotation(
20
+ NomAttributeur.attribut_par(fichier_analyse.extension,
21
+ fichier_analyse.date_extraite)
22
+ )
23
+
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/regex_helpers"
4
+
5
+ module Catalogage
6
+ module Etape
7
+ # Permet de vérifier les fichiers en conflit dans la destination
8
+ class Verificateur
9
+ def initialize(index_fichiers_destination = [], index_fichiers_analyses = [], fichiers_conflit = {})
10
+ @index_fichiers_destination = index_fichiers_destination
11
+ @index_fichiers_analyses = index_fichiers_analyses
12
+ @fichiers_conflit = fichiers_conflit
13
+ @log = Logging.logger["Verificateur"]
14
+ end
15
+
16
+ def index(fichiers_analyses, path_destination)
17
+ index_destination(path_destination)
18
+ index_fichiers_analyses(fichiers_analyses, path_destination)
19
+ @index_fichiers_analyses.each do |fichier_analyse|
20
+ @fichiers_conflit.store(fichier_analyse, @index_fichiers_destination.include?(fichier_analyse))
21
+ end
22
+ @fichiers_conflit
23
+ end
24
+
25
+ def conflit
26
+ @fichiers_conflit.select { |_k, v| v == true }
27
+ end
28
+
29
+ private
30
+
31
+ def index_fichiers_analyses(fichiers_analyses, path_destination)
32
+ fichiers_analyses.each_value do |fichiers|
33
+ fichiers.each do |fichier|
34
+ @index_fichiers_analyses.push fichier.path_nouveau_nom("#{path_destination}/#{fichier.path_destination}")
35
+ end
36
+ end
37
+ end
38
+
39
+ def index_destination(destination)
40
+ Dir.each_child(destination) do |nom_fichier|
41
+ fichier = "#{destination}/#{nom_fichier}"
42
+ if File.file?(fichier)
43
+ @index_fichiers_destination.push fichier if File.extname(fichier) =~ RegexHelpers::EXTENSIONS_EXCLUS
44
+ else
45
+ index_destination(fichier)
46
+ next
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/directory_helpers"
4
+
5
+ require "images/classification/classification"
6
+
7
+ module Catalogage
8
+ # Fichier
9
+ class Fichier
10
+ attr_writer :nom_attribue
11
+ attr_reader :extension, :date_extraite, :exif, :path
12
+
13
+ def initialize(path, extension, date_extraite, exif, nom_attribue = nil)
14
+ @date_extraite = date_extraite
15
+ @path = path
16
+ @extension = extension
17
+ @type = Classification.get_type(extension)
18
+ @exif = exif
19
+ @nom_attribue = nom_attribue
20
+ end
21
+
22
+ def path_destination
23
+ if !@date_extraite.nil?
24
+ "#{@date_extraite.year}/#{format("%02d",
25
+ @date_extraite.month)}/#{DirectoryHelpers.defini_dossier_par(@extension)}"
26
+ else
27
+ "none/#{DirectoryHelpers.defini_dossier_par(@extension)}"
28
+ end
29
+ end
30
+
31
+ def path_nouveau_nom(destination)
32
+ "#{destination}/#{@nom_attribue}#{@extension.downcase}"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Classification
4
+ module Classification
5
+ PHOTO = "photo"
6
+ VIDEO = "video"
7
+ OTHER = "other"
8
+
9
+ EXTENSIONS_PAR_PREFIXE = {
10
+ /jpg|jpeg|png|raw/ => PHOTO,
11
+ /mp4|mov/ => VIDEO
12
+ }.freeze
13
+
14
+ def self.get_type(extension)
15
+ type = OTHER
16
+ EXTENSIONS_PAR_PREFIXE.each_pair do |key, value|
17
+ type = value if key =~ extension[/[a-zA-Z0-9]+/].downcase
18
+ end
19
+ type
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Exif Manipulateur
4
+ module ExifManipulateur
5
+ # Exif Manipulateur erreur
6
+ class ExifManipulateurErreur < StandardError; end
7
+
8
+ def set_datetimeoriginal(_fichier, _datetimeoriginal)
9
+ raise "Non implémenté"
10
+ end
11
+
12
+ def datetimeoriginal?(_fichier)
13
+ raise "Non implémenté"
14
+ end
15
+
16
+ def get_datetimeoriginal(_fichier)
17
+ raise "Non implémenté"
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mini_exiftool"
4
+
5
+ require "images/exif/exif_manipulateur"
6
+
7
+ # Exif Manipulateur
8
+ class MiniExiftoolManipulateur
9
+ include ExifManipulateur
10
+ def set_datetimeoriginal(fichier, datetimeoriginal)
11
+ exif = MiniExiftool.new(fichier)
12
+ exif.datetimeoriginal = datetimeoriginal
13
+ exif.save
14
+ exif
15
+ rescue MiniExiftool::Error => e
16
+ raise ExifManipulateur::ExifManipulateurErreur, e
17
+ end
18
+
19
+ def datetimeoriginal?(fichier)
20
+ !MiniExiftool.new(fichier).datetimeoriginal.nil?
21
+ rescue MiniExiftool::Error => e
22
+ raise ExifManipulateur::ExifManipulateurErreur, e
23
+ end
24
+
25
+ def get_datetimeoriginal(fichier)
26
+ DateTime.parse(MiniExiftool.new(fichier).datetimeoriginal.to_s)
27
+ rescue MiniExiftool::Error => e
28
+ raise ExifManipulateur::ExifManipulateurErreur, e
29
+ end
30
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ # Erreur d'extraction
6
+ class ExtractionErreur < StandardError; end
7
+
8
+ # Extracteur par date
9
+ class ExtracteurParDate
10
+ attr_accessor :regex_patterns
11
+
12
+ def initialize
13
+ @regex_patterns = {
14
+ /(IMG|VID|PANO)_([0-9]{8})_([0-9]{6})/ => Extraction.new.method(:extract01),
15
+ /(IMG|VID|PANO)_([0-9]{8})_([0-9]{4})/ => Extraction.new.method(:extract02),
16
+ /(IMG|VID)_([0-9]{8})_([0-9]{4})-([0-9]{2})/ => Extraction.new.method(:extract02),
17
+ /(IMG|VID)_([0-9]{14})/ => Extraction.new.method(:extract03),
18
+ /([0-9]{8})_([0-9]{6})_([0-9]{3})/ => Extraction.new.method(:extract04),
19
+ /([0-9]{8})_([0-9]{6})_([0-9]{3}) (\([0-9]\))/ => Extraction.new.method(:extract04),
20
+ /([0-9]{8})_([0-9]{6})/ => Extraction.new.method(:extract04),
21
+ /^(Resized)_([0-9]{8})_([0-9]{6})_([0-9]{5})$/ => Extraction.new.method(:extract04),
22
+ /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}\.[0-9]{2}\.[0-9]{2}/ => Extraction.new.method(:extract05),
23
+ /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2})\.([0-9]{2})\.([0-9]{2})-([0-9]{1})$/ => Extraction.new.method(:extract05),
24
+ /([0-9]{2})-([0-9]{2})-([0-9]{4}) ([0-9]{2})-([0-9]{2})-([0-9]{2})/ => Extraction.new.method(:extract08),
25
+ /(PHOTO|Photo|photo|VIDEO|Video|video)-([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2})/ => Extraction.new.method(:extract06),
26
+ /(photo)_([0-9]{4})_([0-9]{2})_([0-9]{2})-([0-9]{2})_([0-9]{2})_([0-9]{2})/ => Extraction.new.method(:extract07),
27
+ /(photo)_([0-9]{4})_([0-9]{2})_([0-9]{2})-([0-9]{2})_([0-9]{2})_([0-9]{2})-([0-9]{2})/ => Extraction.new.method(:extract07)
28
+ }
29
+ end
30
+
31
+ def extraction_du(nom)
32
+ regex_patterns.each_pair do |key, value|
33
+ next unless key =~ nom
34
+
35
+ return value.call(nom)
36
+ end
37
+ raise ExtractionErreur, "Aucune date ne peux être extraite sur '#{nom}'"
38
+ end
39
+
40
+ def extirpabilite(nom)
41
+ est_extirpable = false
42
+ regex_patterns.each_key do |key|
43
+ est_extirpable = true if key =~ nom
44
+ end
45
+ est_extirpable
46
+ end
47
+ end
48
+
49
+ # Extraction
50
+ class Extraction
51
+ def extract01(nom)
52
+ nom_split = nom.split("_")
53
+ date = nom_split[1]
54
+ instant = nom_split[2]
55
+ DateTime.parse("#{date}T#{instant}")
56
+ end
57
+
58
+ def extract02(nom)
59
+ nom_split = nom.split("_")
60
+ date = nom_split[1]
61
+ annee = date[4..7]
62
+ mois = date[2..3]
63
+ jour = date[0..1]
64
+ instant = nom_split[2].split("-")[0]
65
+ DateTime.parse("#{annee}#{mois}#{jour}T#{instant}00")
66
+ end
67
+
68
+ def extract03(nom)
69
+ nom_split = nom.split("_")
70
+ date = nom_split[1][0..7]
71
+ instant = nom_split[1][8..13]
72
+ DateTime.parse("#{date}T#{instant}")
73
+ end
74
+
75
+ def extract04(nom)
76
+ nom_split = nom.split("_")
77
+ if nom =~ /Resized/
78
+ DateTime.parse("#{nom_split[1]}T#{nom_split[2]}")
79
+ else
80
+ DateTime.parse("#{nom_split[0]}T#{nom_split[1]}")
81
+ end
82
+ end
83
+
84
+ def extract05(nom)
85
+ nom_split = nom.split(" ")
86
+ date = nom_split[0].gsub("-", "")
87
+ instant = nom_split[1].gsub(".", "").split("-")[0]
88
+ DateTime.parse("#{date}T#{instant}")
89
+ end
90
+
91
+ def extract06(nom)
92
+ nom_split = nom.split("-")
93
+ date = "#{nom_split[1]}#{nom_split[2]}#{nom_split[3]}"
94
+ seconde = nom_split[6][0..1]
95
+ instant = "#{nom_split[4]}#{nom_split[5]}#{seconde}"
96
+ DateTime.parse("#{date}T#{instant}")
97
+ end
98
+
99
+ def extract07(nom)
100
+ nom_split = nom.split("-")
101
+ date_split = nom_split[0].split("_")
102
+ date = "#{date_split[1]}#{date_split[2]}#{date_split[3]}"
103
+ instant_split = nom_split[1].split("_")
104
+ seconde = instant_split[2][0..1]
105
+ instant = "#{instant_split[0]}#{instant_split[1]}#{seconde}"
106
+ DateTime.parse("#{date}T#{instant}")
107
+ end
108
+
109
+ def extract08(nom)
110
+ nom_split = nom.split(" ")
111
+ date_split = nom_split[0].split("-")
112
+ date = "#{date_split[2]}#{date_split[1]}#{date_split[0]}"
113
+ instant = nom_split[1].gsub("-", "")
114
+ DateTime.parse("#{date}T#{instant}")
115
+ end
116
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "date"
5
+
6
+ # Directory
7
+ module DirectoryHelpers
8
+ def self.get_date(nom_dossier)
9
+ chemins_dossier = nom_dossier.split("/")
10
+ annee = chemins_dossier.select { |v| v =~ /^([0-9]{4})$/ }
11
+ mois = chemins_dossier.select { |v| v =~ /^([0-9]{2})$/ }
12
+ DateTime.new(annee[0].to_i, mois[0].to_i, 1, 0, 0, 0)
13
+ end
14
+
15
+ def self.cree_le(dossier)
16
+ FileUtils.mkdir_p(dossier) if dossier != "" && !Dir.exist?(dossier)
17
+ dossier
18
+ end
19
+
20
+ def self.defini_dossier_par(extension)
21
+ if extension.downcase == ".jpeg"
22
+ "JPG"
23
+ else
24
+ extension[/[a-zA-Z0-9]+/].upcase
25
+ end
26
+ end
27
+
28
+ def self.nombre_fichiers(dossier)
29
+ Dir.glob(File.join(dossier, "**", "*")).select { |file| File.file?(file) }.count
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RegexHelpers
4
+ EXTENSIONS_EXCLUS = /\.(?!(ini|log|1|db-shm|db-wal|db)$)([^.]+$)/.freeze
5
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/classification/classification"
4
+
5
+ # Dedoublonneur
6
+ class Dedoublonneur
7
+ attr_accessor :noms_attribues
8
+
9
+ def initialize(noms_attribues = [], noms_attribues_par_extension = {})
10
+ @noms_attribues = noms_attribues
11
+ @noms_attribues_par_extension = noms_attribues_par_extension
12
+ end
13
+
14
+ def dedoublonne_par_numerotation(nom)
15
+ i = 0
16
+ nom_numerote = nom
17
+ @noms_attribues.each do |nom_attribue|
18
+ if nom_numerote == nom_attribue
19
+ i += 1
20
+ nom_numerote = format("%s-%02d", nom, i)
21
+ end
22
+ end
23
+ @noms_attribues.push(nom_numerote)
24
+ nom_numerote
25
+ end
26
+
27
+ def attribution_par_numero(extension)
28
+ type = Classification.get_type(extension)
29
+ nom_numerote = ""
30
+ if @noms_attribues_par_extension.key?(type)
31
+ numeros_attribues = @noms_attribues_par_extension.fetch(type)
32
+ numero = numeros_attribues.length + 1
33
+ nom_numerote = format("%03d", numero)
34
+ numeros_attribues.push(nom_numerote)
35
+ @noms_attribues_par_extension.merge!({ type => numeros_attribues })
36
+ else
37
+ numero = 1
38
+ nom_numerote = format("%03d", numero)
39
+ numeros_attribues = []
40
+ numeros_attribues.push(nom_numerote)
41
+ @noms_attribues_par_extension.store(type, numeros_attribues)
42
+ end
43
+ nom_numerote
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/classification/classification"
4
+
5
+ # NomAttributeur
6
+ module NomAttributeur
7
+ def self.attribut_par(extension, date)
8
+ prefixe = Classification.get_type(extension)
9
+ date.strftime("#{prefixe}_%Y_%m_%d-%H_%M_%S")
10
+ end
11
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ require "images/helpers/regex_helpers"
6
+
7
+
8
+ module Restauration
9
+ module Etape
10
+ # Definit l'étape d'analyse
11
+ class Analyse
12
+ attr_reader :dossiers_analyses, :nombre_fichiers_analyses
13
+
14
+ def initialize(extracteur, noms_extirpable_par_dossier = {}, dossiers_analyses = {})
15
+ @extracteur = extracteur
16
+ @noms_extirpable_par_dossier = noms_extirpable_par_dossier
17
+ @dossiers_analyses = dossiers_analyses
18
+ @log = Logging.logger[self]
19
+ @nombre_fichiers_analyses = 0
20
+ end
21
+
22
+ def parcours(dossier)
23
+ @log.debug "Parcours du dossier '#{dossier}'"
24
+ Dir.each_child(dossier) do |nom_fichier|
25
+ fichier = "#{dossier}/#{nom_fichier}"
26
+ if File.file?(fichier)
27
+ if File.extname(fichier) =~ RegexHelpers::EXTENSIONS_EXCLUS
28
+ extirpabilite_par(dossier, fichier)
29
+ @nombre_fichiers_analyses += 1
30
+ else
31
+ @log.warn "Le fichier '#{fichier}' ne sera pas analysé"
32
+ end
33
+ else
34
+ parcours(fichier)
35
+ next
36
+ end
37
+ end
38
+ if @noms_extirpable_par_dossier.key?(dossier)
39
+ taux = calcul_taux_d_extirpabilite_par(dossier)
40
+ @dossiers_analyses.store(dossier, taux)
41
+ @log.info "Le dossier '#{dossier}' posséde un taux de #{taux}% d'extirpabilité"
42
+ end
43
+ end
44
+
45
+ def calcul_taux_d_extirpabilite_par(nom_dossier)
46
+ noms_extirpable = @noms_extirpable_par_dossier.fetch(nom_dossier)
47
+ noms_extirpable_possible = @noms_extirpable_par_dossier.fetch(nom_dossier).tally
48
+ if noms_extirpable_possible.key?(true)
49
+ ((noms_extirpable_possible[true] * 100) / noms_extirpable.length)
50
+ else
51
+ 0
52
+ end
53
+ end
54
+
55
+ def extirpabilite_par(path_dossier, nom_fichier)
56
+ extirpable = @extracteur.extirpabilite(nom_fichier)
57
+ nom_extirpable_par_dossier = if @noms_extirpable_par_dossier.key?(path_dossier)
58
+ @noms_extirpable_par_dossier.merge!({ path_dossier => @noms_extirpable_par_dossier
59
+ .fetch(path_dossier)
60
+ .push(extirpable) })
61
+ else
62
+ @noms_extirpable_par_dossier.merge!({ path_dossier => [].push(extirpable) })
63
+ end
64
+ @log.debug "Le fichier '#{nom_fichier}' est extirpable : #{extirpable}"
65
+ nom_extirpable_par_dossier
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ module Restauration
6
+ module Etape
7
+ # Définit l'étape d'application
8
+ class Application
9
+ def initialize(exif_manipulateur)
10
+ @exif_manipulateur = exif_manipulateur
11
+ @log = Logging.logger[self]
12
+ end
13
+
14
+ def parcours(fichiers)
15
+ fichiers.each_pair do |key, value|
16
+ @log.debug "Application sur le fichier '#{key}'"
17
+ if File.file?(key)
18
+ begin
19
+ @exif_manipulateur.set_datetimeoriginal(key, value.date)
20
+ File.rename(key, value.path_nouveau_nom)
21
+ FileUtils.mkdir_p(File.dirname(value.path_destination))
22
+ FileUtils.move(value.path_nouveau_nom, value.path_destination)
23
+ rescue ExifManipulateur::ExifManipulateurErreur => e
24
+ @log.fatal e.message
25
+ rescue SystemCallError => e
26
+ @log.fatal e.message
27
+ end
28
+ else
29
+ @log.warn "le fichier '#{key}' ne sera pas traite"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ require "images/restauration/fichier"
6
+
7
+ require "images/nommage/dedoublonneur"
8
+ require "images/nommage/nom_attributeur"
9
+
10
+ module Restauration
11
+ module Etape
12
+ # Définit l'étape de traitement de dossier extirpable
13
+ class TraitementDossierExtirpable
14
+ attr_reader :fichiers
15
+
16
+ def initialize(extracteur, fichiers = {})
17
+ @extracteur = extracteur
18
+ @fichiers = fichiers
19
+ @log = Logging.logger[self]
20
+ end
21
+
22
+ def parcours(dossiers)
23
+ dossiers.each do |dossier|
24
+ @log.info "Traitement sur le dossier '#{dossier}'"
25
+ dedoublonneur = Dedoublonneur.new
26
+ Dir.each_child(dossier) do |nom_fichier|
27
+ fichier = "#{dossier}/#{nom_fichier}"
28
+ @log.debug "Traitement sur le fichier '#{fichier}'"
29
+ date_extraite = @extracteur.extraction_du(File.basename(fichier, File.extname(fichier)))
30
+ nom_attribue = dedoublonneur.dedoublonne_par_numerotation(NomAttributeur.attribut_par(File.extname(fichier),
31
+ date_extraite))
32
+ @fichiers.store(fichier,
33
+ Fichier.new(nom_attribue, date_extraite, File.dirname(fichier), File.extname(fichier)))
34
+ rescue ExtractionErreur => e
35
+ @log.error e.message
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ require "images/helpers/directory_helpers"
6
+
7
+ require "images/nommage/dedoublonneur"
8
+
9
+ require "images/restauration/fichier"
10
+
11
+ module Restauration
12
+ module Etape
13
+ # Définit l'étape de traitement de dossier non extirpable
14
+ class TraitementDossierNonExtirpable
15
+ attr_reader :fichiers
16
+
17
+ def initialize(fichiers = {})
18
+ @fichiers = fichiers
19
+ @log = Logging.logger[self]
20
+ end
21
+
22
+ def parcours(dossiers)
23
+ dossiers.each do |dossier|
24
+ @log.info "Traitement sur le dossier '#{dossier}'"
25
+ dedoublonneur = Dedoublonneur.new
26
+ Dir.each_child(dossier) do |nom_fichier|
27
+ fichier = "#{dossier}/#{nom_fichier}"
28
+ @log.debug "Traitement sur le fichier '#{fichier}'"
29
+ if File.file?(fichier)
30
+ numero_attribue = dedoublonneur.attribution_par_numero(File.extname(fichier))
31
+ fichiers.store(
32
+ fichier,
33
+ Fichier.new(
34
+ numero_attribue, DirectoryHelpers.get_date(File.dirname(fichier)),
35
+ File.dirname(fichier), File.extname(fichier)
36
+ )
37
+ )
38
+ else
39
+ @log.warn "le fichier '#{fichier}' ne sera pas traite"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/directory_helpers"
4
+ require "images/classification/classification"
5
+
6
+
7
+ module Restauration
8
+ # Fichier
9
+ class Fichier
10
+ attr_reader :nom_attribue, :date, :path, :extension, :type
11
+
12
+ def initialize(nom_attribue, date, path, extension)
13
+ @nom_attribue = nom_attribue
14
+ @date = date
15
+ @path = path
16
+ @extension = extension
17
+ @type = Classification.get_type(extension)
18
+ end
19
+
20
+ def path_nouveau_nom
21
+ "#{@path}/#{@nom_attribue}#{@extension.downcase}"
22
+ end
23
+
24
+ def path_destination
25
+ "#{@path}/#{DirectoryHelpers.defini_dossier_par(@extension)}/#{@nom_attribue}#{@extension.downcase}"
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Restauration
4
+ # Restauration
5
+ class Restore
6
+ def initialize(analyse, traitement_dossier_extirpable, traitement_dossier_non_extirpable, application)
7
+ @analyse = analyse
8
+ @traitement_dossier_extirpable = traitement_dossier_extirpable
9
+ @traitement_dossier_non_extirpable = traitement_dossier_non_extirpable
10
+ @application_photos = application
11
+ @log = Logging.logger[self]
12
+ end
13
+
14
+ def process(dossier, apply)
15
+ @analyse.parcours(dossier)
16
+
17
+ dossiers_extirpable = @analyse.dossiers_analyses.select { |_key, value| value == 100 }.keys
18
+ @traitement_dossier_extirpable.parcours(dossiers_extirpable)
19
+
20
+ dossiers_non_extirpable = @analyse.dossiers_analyses.select { |_key, value| value < 100 }.keys
21
+ @traitement_dossier_non_extirpable.parcours(dossiers_non_extirpable)
22
+
23
+ all_fichiers = @traitement_dossier_extirpable.fichiers.merge(@traitement_dossier_non_extirpable.fichiers)
24
+
25
+ @log.info "Nombre de fichiers analysés : #{@analyse.nombre_fichiers_analyses}"
26
+ @log.info "Nombre de fichiers traités : #{all_fichiers.length}"
27
+
28
+ if apply && all_fichiers.length == @analyse.nombre_fichiers_analyses
29
+ @application_photos.parcours(all_fichiers)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jon
4
+ VERSION = "1.0.0"
5
+ end
data/sig/jon.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Jon
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jon
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - JordanC0TT
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-03-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: debase
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.4.1
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: mocha
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.13.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.13.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-core
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.11.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.11.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-parameterized
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.9.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.9.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: ruby-debug-ide
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.7.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.7.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: solargraph
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.44.3
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.44.3
111
+ - !ruby/object:Gem::Dependency
112
+ name: logging
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 2.3.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 2.3.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: mini_exiftool_vendored
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 9.2.7.v1
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 9.2.7.v1
139
+ description: Permet de cataloguer, restaurer des fichiers photos et vidéos
140
+ email:
141
+ - jordan.content@pm.me
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".rspec"
147
+ - ".rubocop.yml"
148
+ - Gemfile
149
+ - Gemfile.lock
150
+ - README.md
151
+ - Rakefile
152
+ - jon.gemspec
153
+ - lib/images/catalogage/catalog.rb
154
+ - lib/images/catalogage/etape/analyse.rb
155
+ - lib/images/catalogage/etape/application.rb
156
+ - lib/images/catalogage/etape/nom_attribuer.rb
157
+ - lib/images/catalogage/etape/verificateur.rb
158
+ - lib/images/catalogage/fichier.rb
159
+ - lib/images/classification/classification.rb
160
+ - lib/images/exif/exif_manipulateur.rb
161
+ - lib/images/exif/mini_exiftool_manipulateur.rb
162
+ - lib/images/extraction/extracteur_par_date.rb
163
+ - lib/images/helpers/directory_helpers.rb
164
+ - lib/images/helpers/regex_helpers.rb
165
+ - lib/images/nommage/dedoublonneur.rb
166
+ - lib/images/nommage/nom_attributeur.rb
167
+ - lib/images/restauration/etape/analyse.rb
168
+ - lib/images/restauration/etape/application.rb
169
+ - lib/images/restauration/etape/traitement_dossier_extirpable.rb
170
+ - lib/images/restauration/etape/traitement_dossier_non_extirpable.rb
171
+ - lib/images/restauration/fichier.rb
172
+ - lib/images/restauration/restore.rb
173
+ - lib/jon/version.rb
174
+ - sig/jon.rbs
175
+ homepage: https://github.com/JordanC0TT/jon
176
+ licenses: []
177
+ metadata:
178
+ homepage_uri: https://github.com/JordanC0TT/jon
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: 2.6.0
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ requirements: []
194
+ rubygems_version: 3.1.4
195
+ signing_key:
196
+ specification_version: 4
197
+ summary: Catalogue des fichiers
198
+ test_files: []