Passed
Pull Request — master (#443)
by Jaisen
05:38
created

sqlite.SQLite.generate_db()   A

Complexity

Conditions 2

Size

Total Lines 12
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 11
nop 1
dl 0
loc 12
rs 9.85
c 0
b 0
f 0
1
"""
2
SQLite plugin object.
3
This plugin stores metadata about all media in an sqlite database.
4
5
You'll need to include [PluginSQLite] in your config file.
6
By default, the sqlite database will be created in your application 
7
    directory. If you want to specify a different path then 
8
    specify a fully qualified `database_file` path.
9
10
```
11
[PluginSQLite]
12
database_file=/path/to/database.db
13
14
.. moduleauthor:: Jaisen Mathai <[email protected]>
15
"""
16
from __future__ import print_function
17
18
import json
19
import os
20
import sqlite3
21
import time
22
#import json
23
24
#from google_auth_oauthlib.flow import InstalledAppFlow
25
#from google.auth.transport.requests import AuthorizedSession
26
#from google.oauth2.credentials import Credentials
27
28
from elodie.geolocation import place_name
29
from elodie.localstorage import Db
30
from elodie.media.base import Base, get_all_subclasses
31
from elodie.plugins.plugins import PluginBase
32
33
class SQLite(PluginBase):
34
    """A class to execute plugin actions.
35
       
36
       Requires a config file with the following configurations set.
37
       database_file:
38
            The full path to the SQLite database (.db).
39
    
40
    """
41
42
    __name__ = 'SQLite'
43
44
    def __init__(self):
45
        super(SQLite, self).__init__()
46
        
47
        # Default the database file to be in the application plugin directory.
48
        # Override with value from config file.
49
        self.database_file = '{}/plugins/{}/elodie.db'.format(
50
            self.application_directory,
51
            __name__.lower()
52
        )
53
        if('database_file' in self.config_for_plugin):
54
            self.database_file = self.config_for_plugin['database_file']
55
56
        self.con = sqlite3.connect(self.database_file)
57
        self.con.row_factory = sqlite3.Row
58
        self.cursor = self.con.cursor()
59
        if(not self._validate_schema()):
60
            self._create_schema()
61
62
    def after(self, file_path, destination_folder, final_file_path, metadata):
63
        self._upsert(file_path, destination_folder, final_file_path, metadata)
64
65
    def batch(self):
66
        pass
67
68
    def before(self, file_path, destination_folder):
69
        pass
70
71
    def generate_db(self):
72
        db = Db()
73
        for checksum, file_path in db.all():
74
            subclasses = get_all_subclasses()
75
            media = Base.get_class_by_file(file_path, get_all_subclasses())
76
            media.set_checksum(
77
                db.checksum(file_path)
78
            )
79
            metadata = media.get_metadata()
80
            destination_folder = os.path.dirname(file_path)
81
            final_file_path = '{}{}'.format(os.path.sep, os.path.basename(file_path))
82
            self._upsert(file_path, destination_folder, final_file_path, metadata)
83
84
    def _create_schema(self):
85
        self.database_schema = '{}{}{}'.format(
86
            os.path.dirname(os.path.realpath(__file__)),
87
            os.sep,
88
            'schema.sql'
89
        )
90
91
        with open(self.database_schema, 'r') as fp_schema:
92
            sql_statement = fp_schema.read()
93
            self.cursor.executescript(sql_statement)
94
95
    def _insert_row_sql(self, final_path, metadata):
96
        path = '{}/{}.{}'.format(
97
                    metadata['directory_path'],
98
                    metadata['base_name'],
99
                    metadata['extension']
100
                )
101
        return (
102
                """INSERT INTO `metadata` (
103
                    `hash`, `path`, `album`, `camera_make`, `camera_model`,
104
                    `date_taken`, `latitude`, `location_name`, `longitude`,
105
                    `original_name`, `title`, `_modified`)
106
                    VALUES (
107
                    :hash, :path, :album, :camera_make, :camera_model,
108
                    :date_taken, :latitude, :location_name, :longitude,
109
                    :original_name, :title, datetime('now'))""",
110
                self._sql_values(final_path, metadata)
111
        )
112
113
    def _run_query(self, sql, values):
114
        self.cursor.execute(sql, values)
115
        return self.cursor.fetchall()
116
117
    def _sql_values(self, final_path, metadata, current_path=None):
118
        timestamp = int(time.time())
119
        return {
120
                    'hash': metadata['checksum'],
121
                    'path': final_path,
122
                    'album': metadata['album'],
123
                    'camera_make': metadata['camera_make'],
124
                    'camera_model': metadata['camera_model'],
125
                    'date_taken': time.strftime('%Y-%m-%d %H:%M:%S', metadata['date_taken']),
126
                    'latitude': metadata['latitude'],
127
                    'location_name': place_name(metadata['latitude'], metadata['longitude'])['default'],
128
                    'longitude': metadata['longitude'],
129
                    'original_name': metadata['original_name'],
130
                    'title': metadata['title'],
131
                    'current_path': current_path
132
                }
133
134
    def _update_row_sql(self, current_path, final_path, metadata):
135
        timestamp = int(time.time())
136
        return (
137
            """UPDATE `metadata` SET `hash`=:hash, `path`=:path, `album`=:album, `camera_make`=:camera_make,
138
                `camera_model`=:camera_model, `date_taken`=:date_taken, `latitude`=:latitude,
139
                `longitude`=:longitude, `original_name`=:original_name, `title`=:title, `_modified`=datetime('now')
140
                WHERE `path`=:current_path""",
141
            self._sql_values(final_path, metadata, current_path)
142
        )
143
144
    def _upsert(self, file_path, destination_folder, final_file_path, metadata):
145
        # We check if the source path exists in the database already.
146
        # If it does then we assume that this is an update operation.
147
        full_destination_path = '{}{}'.format(destination_folder, final_file_path)
148
        self.cursor.execute("SELECT `path` FROM `metadata` WHERE `path`=:path", {'path': file_path})
149
150
        if(self.cursor.fetchone() is None):
151
            self.log(u'SQLite plugin inserting {}'.format(file_path))
152
            sql_statement, sql_values = self._insert_row_sql(full_destination_path, metadata)
153
        else:
154
            self.log(u'SQLite plugin updating {}'.format(file_path))
155
            sql_statement, sql_values = self._update_row_sql(file_path, full_destination_path, metadata)
156
157
        self.cursor.execute(sql_statement, sql_values)
158
159
    def _validate_schema(self):
160
        try:
161
            self.cursor.execute('SELECT * FROM `metadata` LIMIT 1');
162
            return True
163
        except sqlite3.OperationalError:
164
            return False
165