Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

# encoding: utf-8 

from __future__ import print_function, division, absolute_import 

 

from sqlalchemy.orm import sessionmaker 

from .errors import ConsistencyError 

from .logger import logger 

from .db_objects import SiteDbo, PictureDbo, dbo_from_bunch 

from .utils import iter_to_list 

 

 

def check_and_commit(site, engine): 

exceptions = check(site, engine) 

if exceptions: 

raise ConsistencyError("check for site update failed, see logs for details") 

return commit(site, engine) 

 

 

@iter_to_list 

def check(site, engine): 

logger().debug("called check for site {}".format(site.name)) 

Session = sessionmaker(bind=engine) 

session = Session() 

existing_site_dbo = session.query(SiteDbo).filter(SiteDbo.name == site.name).first() 

 

if existing_site_dbo is not None: 

logger().debug("site with same name {} already exists".format(existing_site_dbo)) 

yield from _check_site_and_picture_modifications(existing_site_dbo, site, session) 

 

 

def commit(site, engine): 

logger().debug("called commit for site {}".format(site.name)) 

Session = sessionmaker(bind=engine) 

session = Session() 

existing_site_dbo = session.query(SiteDbo).filter(SiteDbo.name == site.name).first() 

 

if existing_site_dbo is not None: 

logger().debug("site with same name {} already exists".format(existing_site_dbo)) 

_update_site_and_picture_dbos(existing_site_dbo, site, session) 

else: 

site_row = _create_site_dbo(site) 

logger().debug("add new site row {}".format(site_row)) 

session.add(site_row) 

 

session.commit() 

 

sites = session.query(SiteDbo).filter(SiteDbo.name == site.name).all() 

assert len(sites) == 1 

return sites[0] 

 

 

def _create_site_dbo(site): 

site = site.copy() 

site.pictures = [_create_picture_dbo(p) for p in site.pictures] 

return dbo_from_bunch(SiteDbo, site) 

 

 

def _create_picture_dbo(picture): 

return dbo_from_bunch(PictureDbo, picture) 

 

 

def _check_site_and_picture_modifications(site_dbo, site_from_landing_zone, session): 

 

yield from _check_for_site_modifications(site_dbo, site_from_landing_zone) 

 

(to_create, 

dbos_to_drop, 

dbos_already_existing) = _determine_picture_differences(site_dbo, site_from_landing_zone) 

 

yield from _handle_dropped_pictures(site_dbo, dbos_to_drop) 

yield from _check_picture_modifications(site_dbo, dbos_already_existing, site_from_landing_zone) 

 

 

def _update_site_and_picture_dbos(site_dbo, site_from_landing_zone, session): 

(to_create, 

dbos_to_drop, 

dbos_already_existing) = _determine_picture_differences(site_dbo, site_from_landing_zone) 

_add_new_pictures(site_dbo, to_create) 

 

 

def _check_for_site_modifications(site_dbo, site_from_landing_zone): 

site = site_from_landing_zone 

if any((site_dbo.name != site.name, 

site_dbo.street != site.street, 

site_dbo.city != site.city, 

site_dbo.coord_x != site.coord_x, 

site_dbo.coord_y != site.coord_y, 

site_dbo.coord_z != site.coord_z, 

site_dbo.description != site.description)): 

from .pretty_printers import pretty_log # local import avoids circular import 

error = logger().error 

error("detected site modifications. existing site:") 

pretty_log(error, site_dbo) 

error("detected site modifications. modified site:") 

pretty_log(error, site) 

yield ConsistencyError("detected invalid site modifiations, see log for details") 

 

 

def _determine_picture_differences(site_dbo, site_from_landing_zone): 

 

# this all assumes that the picture file name is unique within one site: 

new_picture_names = {p.filename: p for p in site_from_landing_zone.pictures} 

existing_picture_names = {p.filename: p for p in site_dbo.pictures} 

 

names_to_create = set(new_picture_names) - set(existing_picture_names) 

names_to_drop = set(existing_picture_names) - set(new_picture_names) 

names_already_existing = set(existing_picture_names) & set(new_picture_names) 

 

to_create = list(map(new_picture_names.get, names_to_create)) 

dbos_to_drop = list(map(existing_picture_names.get, names_to_drop)) 

dbos_already_existing = list(map(existing_picture_names.get, names_already_existing)) 

 

return to_create, dbos_to_drop, dbos_already_existing 

 

 

def _handle_dropped_pictures(site_dbo, dbos_to_drop): 

if dbos_to_drop: 

logger().error("detected missing pictures in landing zone") 

for picture in sorted(dbos_to_drop, key=lambda p: p.filename): 

from .pretty_printers import pretty_log # local import avoids circular import 

logger().error("picture removed from landing zone:") 

pretty_log(logger().error, picture) 

yield ConsistencyError("pictures removed from landing zone, see log for details") 

 

 

def _check_picture_modifications(site_dbo, dbos_existing_pictures, site_from_landing_zone): 

new_pictures = {p.filename: p for p in site_from_landing_zone.pictures} 

old_picture_dbos = {p.filename: p for p in dbos_existing_pictures} 

 

invalid_updates = [] 

common_names = new_pictures.keys() & old_picture_dbos.keys() 

for file_name in sorted(common_names): 

old_picture_dbo = old_picture_dbos[file_name] 

new_picture = new_pictures[file_name] 

are_same = _compare_pictures(old_picture_dbo, new_picture) 

if not are_same: 

invalid_updates.append((old_picture_dbo, new_picture)) 

 

if invalid_updates: 

from .pretty_printers import pretty_log # local import avoids circular import 

logger().error("detected not allowd modifications of pictures in landing zone:") 

for old_picture, new_picture in invalid_updates: 

logger().error("existing picture:") 

pretty_log(logger().error, old_picture) 

logger().error("modified picture:") 

pretty_log(logger().error, new_picture) 

 

yield ConsistencyError("detected invalid picture modification, see log for details") 

 

 

def _compare_pictures(picture_dbo, new_picture): 

return all((picture_dbo.filename == new_picture.filename, 

picture_dbo.description == new_picture.description, 

picture_dbo.date == new_picture.date, 

picture_dbo.data == new_picture.data)) 

 

 

def _add_new_pictures(site_dbo, to_create): 

for picture in to_create: 

site_dbo.pictures.append(_create_picture_dbo(picture))