MetaStalk.main   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 191
Duplicated Lines 0 %

Test Coverage

Coverage 98.41%

Importance

Changes 0
Metric Value
wmc 16
eloc 117
dl 0
loc 191
ccs 62
cts 63
cp 0.9841
rs 10
c 0
b 0
f 0

1 Function

Rating   Name   Duplication   Size   Complexity  
B start() 0 82 3

4 Methods

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