Passed
Push — master ( 2bd01a...0f6aab )
by Cyb3r
04:52 queued 12s
created

MetaStalk.main.MetaStalk.run()   A

Complexity

Conditions 4

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 16
nop 2
dl 0
loc 28
ccs 9
cts 9
cp 1
crap 4
rs 9.6
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2 1
"""Main function of MetaStalk.
3
Run get any metadata from photos
4
and creates graphs from the metadata using MetaStalk.Modules
5
"""
6 1
import argparse
7 1
from collections import OrderedDict
8 1
import os
9 1
import logging
10 1
import timeit
11 1
import exifreader
12 1
from cyberjake import check_update
13 1
from MetaStalk import __version__
14 1
import MetaStalk.utils as utils
15 1
import MetaStalk.modules as modules
16
17
18 1
class MetaStalk:
19
    """MetaStalk.
20
    ---
21
22
    Main Class for all MetaStalk work
23
    """
24
25 1
    def __init__(self):
26 1
        self.log = logging.getLogger("MetaStalk")
27 1
        self.t_start = timeit.default_timer()
28 1
        self.valid, self.invalid = [], []
29 1
        self.plots = {}
30
31 1
    def run(self, args: argparse.Namespace) -> None:
32
        """Run function.
33
34
        Process files and passes the information on utils.graph to generates graphs.
35
        Will also pass plots to utils.export if the flag is set.
36
37
        Parameters
38
        ----------
39
        args : argparse.Namespace
40
            The arguments that were passed from command line
41
        """
42 1
        self.parse_files(args.files)
43
44 1
        self.plots = {
45
            "Stats": modules.stats(self.valid, self.invalid),
46
            "GPS": modules.gps_check(self.valid),
47
            "Timestamp": modules.date_time(self.valid),
48
            "Model": modules.pie_chart(self.valid, "Image Model"),
49
            "Manufacturer": modules.pie_chart(self.valid, "Image Make"),
50
            "Focal": modules.pie_chart(self.valid, "EXIF FocalLength"),
51
            "Producer": modules.pie_chart(self.valid, "Image Software"),
52
        }
53 1
        if args.alphabetic:
54 1
            self.plots = OrderedDict(sorted(self.plots.items()))
55 1
        if args.export:
56 1
            utils.export(args.export, args.output, self.plots)
57 1
        if not args.export_only:
58 1
            utils.graph(self.plots, self.t_start, args.test, args.no_open)
59
60 1
    def parse_files(self, path_list: list) -> None:
61
        """Parses the files.
62
         Use to complete the directory parsing and file adding. Does not return anything
63
         but adds the files to to either the invalid or valid list.
64
65
        Parameters
66
        ----------
67
        path_list : list
68
            The list of paths to search for files
69
        """
70 1
        for path in path_list:
71 1
            if os.path.isdir(path):
72 1
                self.log.debug("Detected path as a directory")
73 1
                for root, _, files in os.walk(path):
74 1
                    for item in files:
75 1
                        item_path = os.path.join(root, item)
76 1
                        self.exif_check(item_path)
77
            else:
78 1
                self.exif_check(path)
79
80 1
    def exif_check(self, file_path: str) -> None:
81
        """exif_check.
82
83
        Used to append files if the path is not a directory.
84
85
        Parameters
86
        ----------
87
        file_path : str
88
            The path of the file to check to see if it has exif metadata
89
90
        Returns
91
        -------
92
93
        """
94 1
        with open(file_path, "rb") as f:
95 1
            tags = exifreader.process_file(f)
96 1
            f.close()
97 1
        if tags:
98 1
            tags["item"] = file_path
99 1
            self.valid.append(tags)
100 1
            self.log.debug("%s has metadata", file_path)
101
        else:
102 1
            self.invalid.append(file_path)
103 1
            self.log.debug("%s has no metadata data", file_path)
104
105
106 1
def start():
107
    """Start
108
    ---
109
110
    Function needed to start MetaStalk. Does all the argument parsing.
111
    """
112 1
    parser = argparse.ArgumentParser(
113
        prog="MetaStalk", description="Tool to graph " "image metadata."
114
    )
115 1
    parser.add_argument(
116
        "files", nargs="*", default=None, help="Path of photos to check."
117
    )
118 1
    parser.add_argument(
119
        "-a",
120
        "--alphabetic",
121
        help="Sorts charts in alphabetical order rather than" " the default order",
122
        default=False,
123
        action="store_true",
124
    )
125 1
    parser.add_argument(
126
        "-d",
127
        "--debug",
128
        help="Sets logging level to DEBUG.",
129
        action="store_const",
130
        dest="loglevel",
131
        const=logging.DEBUG,
132
        default=logging.WARNING,
133
    )
134 1
    parser.add_argument(
135
        "-e",
136
        "--export",
137
        choices=["pdf", "svg", "webp", "jpeg", "png", "html", "html_offline"],
138
        help="Exports the graphs rather than all on one web page",
139
    )
140 1
    parser.add_argument(
141
        "--export-only",
142
        help="Makes it so that MetaStalk only export",
143
        default=False,
144
        action="store_true",
145
    )
146 1
    parser.add_argument(
147
        "--no-open",
148
        help="Will only start the server and not open the browser" " to view it",
149
        default=False,
150
        action="store_true",
151
    )
152 1
    parser.add_argument(
153
        "-o",
154
        "--output",
155
        default="metastalk_exports",
156
        help="The name of the directory to output exports to. "
157
        "Will be created if it does not exist. "
158
        "Defaults to metastalk_exports.",
159
    )
160 1
    parser.add_argument(
161
        "-t",
162
        "--test",
163
        default=False,
164
        action="store_true",
165
        help="Does not show the graphs at the end.",
166
    )
167 1
    parser.add_argument(
168
        "-v",
169
        "--verbose",
170
        help="Sets logging level to INFO",
171
        action="store_const",
172
        dest="loglevel",
173
        const=logging.INFO,
174
    )
175 1
    args = parser.parse_args()
176 1
    log = utils.make_logger("MetaStalk", args.loglevel)
177 1
    log.info("MetaStalk starting")
178 1
    if check_update("MetaStalk", __version__):
179
        log.warning(
180
            "There is a newer version of MetaStalk available.\n"
181
            "Run pip3 install -U metastalk"
182
        )
183 1
    if not args.files:
184 1
        log.error("ERROR: No path was inputted.")
185 1
        raise FileNotFoundError("No path was inputted.")
186
    metastalk = MetaStalk()  # pragma: no cover
187
    metastalk.run(args)  # pragma: no cover
188